package com.web.webstart.base.util.jpa;

import com.alibaba.fastjson.JSON;
import com.web.exiaodao.business.constant.JConstant;
import com.web.webstart.base.util.XaUtil;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class DynamicSpecifications {
    private static final ConversionService conversionService = new DefaultConversionService();

    @SuppressWarnings("rawtypes")
    public static <T> Specification<T> bySearchFilter(
            final Collection<SearchFilter> filters, final Class<T> clazz) {
        return new Specification<T>() {
            @SuppressWarnings("unchecked")
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
                                         CriteriaBuilder builder) {
                if (XaUtil.isNotEmpty(filters)) {
                    List<Predicate> predicates = new ArrayList<Predicate>();
                    for (SearchFilter filter : filters) {

                        String[] names = filter.fieldName.split("\\.");
                        Path expression = root.get(names[0]);
                        for (int i = 1; i < names.length; i++) {
                            expression = expression.get(names[i]);
                        }

                        // convert value from string to target type
                        Class attributeClass = expression.getJavaType();
                        boolean expIsNotStr = !attributeClass.equals(String.class);
                        boolean filterValueIsStr = filter.value instanceof String;
                        boolean attriIsCanConvert = conversionService.canConvert(String.class, attributeClass);

                        if (expIsNotStr && filterValueIsStr && attriIsCanConvert) {
                            if (filter.operator != SearchFilter.Operator.IN && filter.operator != SearchFilter.Operator.BETWEEN) {
                                filter.value = conversionService.convert(filter.value, attributeClass);
                            }
                        }

                        // logic operator
                        switch (filter.operator) {
                            case EQ:
                                predicates.add(builder.equal(expression,
                                        filter.value));
                                break;
                            case NE:
                                predicates.add(builder.notEqual(expression,
                                        filter.value));
                                break;
                            case IN:
                                if (filter.value instanceof String) {
                                    List<Object> list = JSON.parseArray(filter.value.toString(), Object.class);
                                    filter.value = list;
                                }
                                predicates.add(expression.in(((List) filter.value).toArray()));
                                break;
                            case ISNULL:
                                predicates.add(builder.isNull(expression));
                                break;
                            case LIKE:
                                predicates.add(
                                        builder.like(expression, "%" + filter.value + "%")
                                );
                                break;
                            case LIKEIGNORE:
                                predicates.add(
                                        builder.like(builder.lower(expression), "%"
                                                + filter.value.toString().toLowerCase() + "%")
                                );
                                break;
                            case LIKEPINYIN:
                                predicates.add(builder.like(builder.lower(expression), "%"
                                        + filter.value.toString().toLowerCase() + "%"));
                                // TODO:dz 2017/1/20  like匹配中文和汉字
                                break;
                            case GT:
                                predicates.add(builder.greaterThan(expression,
                                        (Comparable) filter.value));
                                break;
                            case LT:
                                predicates.add(builder.lessThan(expression,
                                        (Comparable) filter.value));
                                break;
                            case GTE:
                                predicates.add(builder.greaterThanOrEqualTo(
                                        expression, (Comparable) filter.value));
                                break;
                            case LTE:
                                predicates.add(builder.lessThanOrEqualTo(
                                        expression, (Comparable) filter.value));
                                break;
                            case BETWEEN:
                                predicates.add(builder.between(expression,
                                        (Comparable) filter.value.toString().split(JConstant.SYMBOL_DOT)[0], (Comparable) filter.value.toString().split(",")[1]));
                                break;
                        }
                    }

                    // 将所有条件用 and 联合起来
                    if (predicates.size() > 0) {
                        return builder.and(predicates
                                .toArray(new Predicate[predicates.size()]));
                    }
                }

                return builder.conjunction();
            }
        };
    }
}
